#ifndef MCL_VAL_H_
#define MCL_VAL_H_

#include "mcl/da.h"
#include "mcl/log.h"
#include "mcl/writer.h"
#include "mcl/gc.h"

#define VTYPE_MAP(_) \
	_(NIL, nil) \
	_(BOOL, bool) \
	_(I64, int64) \
	_(STR, string) \
	_(SYM, symbol) \
	_(LIST, list) \
	_(FUNC, func) \
	_(ERROR, error) \
	_(MACRO, macro) \
	_(LAZY, lazy) \
	_(PARAMETER, parameter)

enum vtype {
#define _(NAME, HUMAN_NAME) MC_##NAME,
	VTYPE_MAP(_)
#undef _
};

enum {
	VTYPE_COUNT = 0
#define _(NAME, HUMAN_NAME) +1
	VTYPE_MAP(_)
#undef _
		,
};

struct vtype_info {
	char const *name[VTYPE_COUNT];
};

extern struct vtype_info VTYPE_INFO;

struct pos {
	usize row;
	usize col;
};

#define POS_INIT {.row = 1, .col = 1}

DEFINE_DA(da_pos, struct pos);

struct runtime;
struct env;
typedef struct lvalue *(*fn_builtin)(struct runtime *rt, struct env *env);

DEFINE_DA(da_dstr, struct dstr);

enum functype {
	FUNC_LAMBDA,
	FUNC_MACRO,
};

enum lambdatype {
	LAMBDA_LISP,
	LAMBDA_BUILTIN,
};

struct lambda {
	struct env *env;
	struct da_dstr parameters;
	struct pos pos;

	enum lambdatype type;
	union {
		struct lvalue *body;
		fn_builtin builtin;
	} via;
};

static inline struct lambda *new_lambda_builtin(
	struct env *env,
	struct da_dstr parameters,
	fn_builtin builtin)
{
	struct lambda *lambda = (struct lambda *)malloc(sizeof(struct lambda));

	lambda->env = env;
	lambda->parameters = parameters;
	lambda->pos = (struct pos)POS_INIT;
	lambda->type = LAMBDA_BUILTIN;
	lambda->via.builtin = builtin;

	return lambda;
}

struct lazy {
	struct lvalue *v;
	struct lambda func;
};

struct verror {
	struct dstr message;
};

// 引用 list 的其中一段。
struct slice {
	struct list *list;

	struct lvalue *data;
	usize len;
};

struct lvalue {
	enum vtype type;
	struct pos pos;
	struct dstr_view comment;

	union {
		bool bool_;
		int64_t i64;
		struct dstr str;
		struct dstr sym;
		struct slice list;
		struct lambda *func;
		struct lambda *macro;
		struct verror err;
		struct lazy *lazy;
		usize parameter;
	} via;
};

static inline struct slice slice_tail(struct slice *slice)
{
	if (slice->len == 0) {
		return *slice;
	}

	return (struct slice){.list = slice->list,
			      .data = slice->data + 1,
			      .len = slice->len - 1};
}

struct lvalue_uninit {
	struct lvalue val;
};

#define VAL_INIT \
	{ \
		.gc = GC_INIT, \
		.type = MC_NIL, \
		.pos = POS_INIT, \
		.comment = DSTR_VIEW_INIT, \
	}

// 包含 len 个 struct val 的数组。
struct list {
	usize len;
	usize refcount;

	// list 总是申请在堆上，实际为 list 申请的内存中可能会有多于
	// DA_INIT_CAP 个 val，因此 data 数组允许越界 DA_INIT_CAP。
	struct lvalue data[DA_INIT_CAP];
};

static inline struct lvalue *init_val_nil(struct lvalue_uninit *);

static inline struct list *new_list(usize len)
{
	struct list *l = (struct list *)malloc(
		sizeof(struct list) +
		(len - DA_INIT_CAP) * sizeof(struct lvalue));

	l->refcount = 0;
	l->len = len;

	return l;
}

static inline struct slice make_slice_from_list(struct list *l)
{
	l->refcount += 1;

	return (struct slice){.list = l, .data = l->data, .len = 0};
}

// 创建 slice，指向新申请的长度为 DA_INIT_CAP 的 list。
static inline struct slice make_slice(void)
{
	struct list *l = new_list(DA_INIT_CAP);

	logdebug("make slice, data=%p", l->data);
	return make_slice_from_list(l);
}

static inline void release_list(struct list *l)
{
	l->refcount -= 1;

	if (l->refcount == 0) {
		free(l);
	}
}

static inline struct lvalue_uninit *append(struct slice *slice)
{
	logdebug("append slice, data=%p", slice->data);

	struct lvalue *list_data = slice->list->data;
	usize list_len = slice->list->len;

	// 当 slice 达到了 list 的结尾，申请一个新的 slice，长度是 slice
	// （而不是 list）两倍。
	if ((uintptr_t)(slice->data + slice->len) >=
	    (uintptr_t)(list_data + list_len)) {
		usize newlen = slice->len * 2;
		struct list *newl = new_list(newlen);

		memmove(newl->data,
			slice->data,
			slice->len * sizeof(struct lvalue));
		release_list(slice->list);

		logdebug("new list, data=%p", newl->data);
		slice->list = newl;
		slice->data = newl->data;
	}

	slice->len += 1;
	return (struct lvalue_uninit *)(slice->data + (slice->len - 1));
}

static inline struct lvalue *init_val_nil(struct lvalue_uninit *v)
{
	v->val.type = MC_NIL;
	return &v->val;
}

static inline struct lvalue *init_val_bool(struct lvalue_uninit *v, bool b)
{
	v->val.type = MC_BOOL;
	v->val.via.bool_ = b;
	return &v->val;
}

static inline struct lvalue *
init_val_sym(struct lvalue_uninit *v, struct dstr sym)
{
	v->val.type = MC_SYM;
	v->val.via.sym = sym;
	return &v->val;
}

static inline struct lvalue *init_val_i64(struct lvalue_uninit *v, int64_t x)
{
	v->val.type = MC_I64;
	v->val.via.i64 = x;
	return &v->val;
}

static inline struct lvalue *init_val_list(struct lvalue_uninit *v)
{
	v->val.type = MC_LIST;
	v->val.via.list = make_slice();
	return &v->val;
}

struct lvalue *new_val(struct runtime *rt, enum vtype type);
struct lvalue *
new_builtin(struct env *env, fn_builtin builtin, struct da_dstr args);

void write_val(struct writer *w, usize indent, struct lvalue *v);

void val_dbg(char const *name, struct lvalue *v);

#endif
